{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Gymnasium environments\n",
    "\n",
    "This Section shows how you can make and use the `gym` environments that interface with the simulator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from pathlib import Path\n",
    "import torch\n",
    "import mediapy\n",
    "\n",
    "# Set working directory to the base directory 'gpudrive'\n",
    "working_dir = Path.cwd()\n",
    "while working_dir.name != 'gpudrive':\n",
    "    working_dir = working_dir.parent\n",
    "    if working_dir == Path.home():\n",
    "        raise FileNotFoundError(\"Base directory 'gpudrive' not found\")\n",
    "os.chdir(working_dir)\n",
    "\n",
    "from gpudrive.env.config import EnvConfig\n",
    "from gpudrive.env.env_torch import GPUDriveTorchEnv\n",
    "from gpudrive.visualize.utils import img_from_fig\n",
    "from gpudrive.env.dataset import SceneDataLoader"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "MAX_NUM_OBJECTS = 64  # Maximum number of objects in the scene we control\n",
    "NUM_WORLDS = 2  # Number of parallel environments\n",
    "UNIQUE_SCENES = 2 # Number of unique scenes\n",
    "\n",
    "device = 'cpu' # for simplicity purposes in notebook we use cpu, note that the simulator is optimized for GPU so use cuda if possible"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Initializing environments\n",
    "\n",
    "- We provide both a torch and jax gymnasium interface with the simulator. Most functionality is specified in the `GPUDriveGymEnv` class in the `base_env`, `torch_env` and `jax_env` both inherit from the `GPUDriveGymEnv`, the only difference between these is that one exports torch tensors and the other jax arrays.\n",
    "- All environment settings are defined in the `EnvConfig` dataclass. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "env_config = EnvConfig(\n",
    "    steer_actions = torch.round(\n",
    "        torch.linspace(-1.0, 1.0, 3), decimals=3),\n",
    "    accel_actions = torch.round(\n",
    "        torch.linspace(-3, 3, 3), decimals=3\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "> 💡 **See the gym environment docs at [`pygpudrive/env`](https://github.com/Emerge-Lab/gpudrive/tree/main/pygpudrive/env)**\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Make dataloader\n",
    "data_loader = SceneDataLoader(\n",
    "    root=\"data/processed/examples\", # Path to the dataset\n",
    "    batch_size=NUM_WORLDS, # Batch size, you want this to be equal to the number of worlds (envs) so that every world receives a different scene\n",
    "    dataset_size=UNIQUE_SCENES, # Total number of different scenes we want to use\n",
    "    sample_with_replacement=False, \n",
    "    seed=42, \n",
    "    shuffle=True,   \n",
    ")\n",
    "\n",
    "# Make environment\n",
    "env = GPUDriveTorchEnv(\n",
    "    config=env_config,\n",
    "    data_loader=data_loader,\n",
    "    max_cont_agents=MAX_NUM_OBJECTS, # Maximum number of agents to control per scenario\n",
    "    device=device,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['data/processed/examples/tfrecord-00000-of-01000_325.json',\n",
       " 'data/processed/examples/tfrecord-00000-of-01000_222.json']"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "env.data_batch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rollout \n",
    "\n",
    "A single rollout (one episode) is implemented as follows. We:\n",
    "- step through N worlds simultaneously.\n",
    "- render the simulator state using `plot_simulator_state()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "obs = env.reset()\n",
    "\n",
    "frames = {f\"env_{i}\": [] for i in range(NUM_WORLDS)}\n",
    "\n",
    "for t in range(env_config.episode_len):\n",
    "    \n",
    "    # Sample random actions\n",
    "    rand_action = torch.Tensor(\n",
    "        [[env.action_space.sample() for _ in range(MAX_NUM_OBJECTS * NUM_WORLDS)]]\n",
    "    ).reshape(NUM_WORLDS, MAX_NUM_OBJECTS)\n",
    "\n",
    "    # Step the environment\n",
    "    env.step_dynamics(rand_action)\n",
    "\n",
    "    obs = env.get_obs()\n",
    "    reward = env.get_rewards()\n",
    "    done = env.get_dones()\n",
    "\n",
    "    # Render the environment    \n",
    "    if t % 5 == 0:\n",
    "        imgs = env.vis.plot_simulator_state(\n",
    "            env_indices=list(range(NUM_WORLDS)),\n",
    "            time_steps=[t]*NUM_WORLDS,\n",
    "            zoom_radius=70,\n",
    "        )\n",
    "    \n",
    "        for i in range(NUM_WORLDS):\n",
    "            frames[f\"env_{i}\"].append(img_from_fig(imgs[i])) \n",
    "        \n",
    "    if done.all():\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Display videos of agents taking random actions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table class=\"show_videos\" style=\"border-spacing:0px;\"><tr><td style=\"padding:1px;\"><div style=\"display:flex; align-items:left;\">\n",
       "      <div style=\"display:flex; flex-direction:column; align-items:center;\">\n",
       "      <div>env_0</div><div><img width=\"500\" height=\"500\" style=\"image-rendering:auto; object-fit:cover;\" src=\"\"/></div></div></div></td><td style=\"padding:1px;\"><div style=\"display:flex; align-items:left;\">\n",
       "      <div style=\"display:flex; flex-direction:column; align-items:center;\">\n",
       "      <div>env_1</div><div><img width=\"500\" height=\"500\" style=\"image-rendering:auto; object-fit:cover;\" src=\"\"/></div></div></div></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mediapy.show_videos(frames, fps=5, width=500, height=500, columns=2, codec='gif')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "gpudrive",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
